Statement vs PreparedStatement
contents
다음은 Statement와 PreparedStatement에 대한 비교입니다.
이 용어들은 주로 Java (JDBC) 서 사용되지만, SQL 데이터베이스와 상호 작용하는 거의 모든 프로그래밍 언어(PHP, Python, C# 등)에 동일하게 적용되는 개념입니다.
1. 핵심 정의
Statement
- 정의: 정적인 SQL 쿼리를 실행하기 위한 표준 메커니즘입니다.
- 작동 방식: 완성된 SQL 문자열 전체를 데이터베이스로 전송합니다.
- 특징: SQL 로직(코드)과 데이터(파라미터)가 전송 전에 하나의 긴 텍스트로 합쳐집니다.
PreparedStatement
- 정의: 파라미터화된(동적) SQL 쿼리를 실행하기 위해 사용되는, Statement의 확장된 인터페이스입니다.
- 작동 방식: SQL 로직을 먼저 전송하여 "준비(Prepare)"시키고, 데이터는 나중에 따로 끼워 넣습니다.
- 특징: 코드(SQL) 와 데이터(변수) 를 엄격하게 분리합니다.
2. 실행 아키텍처 (내부 구조)
차이점을 이해하려면, 데이터베이스 엔진이 쿼리를 받았을 때 수행하는 4단계 과정을 알아야 합니다.
- 파싱(Parsing): 문법(철자, 구문) 확인.
- 컴파일(Compiling): 기계가 이해할 수 있는 코드로 변환.
- 최적화(Optimization): 실행 계획(인덱스 사용 여부 등) 수립.
- 실행(Execution): 쿼리 실행 및 데이터 인출.
"Statement"의 처리 방식:
- 상황: 사용자 1,000명을 입력하기 위해 루프를 돕니다.
- 과정: DB는 1, 2, 3, 4 단계를 1,000번 반복합니다.
- 쿼리 텍스트가 조금이라도 다르면(
VALUES ('A')vsVALUES ('B')), DB는 이를 완전히 다른 쿼리로 인식하고 처음부터 다시 분석합니다.
"PreparedStatement"의 처리 방식:
- 상황:
INSERT... VALUES (?)템플릿을 사용하여 1,000번 루프를 돕니다. - 과정:
- Prepare: DB는 1, 2, 3 단계를 딱 한 번 수행합니다. 그리고
INSERT... VALUES (?)에 대한 실행 계획을 캐시에 저장합니다. - Execute: DB는
?자리에 값만 바꿔 끼우며 4단계(실행)를 1,000번 반복합니다.
- Prepare: DB는 1, 2, 3 단계를 딱 한 번 수행합니다. 그리고
- 결과: 데이터베이스 CPU 부하가 획기적으로 줄어듭니다.
3. 가장 큰 차이점: 보안 (SQL Injection)
이것이 가장 중요한 차이점입니다.
"Statement"의 취약점
문자열 결합(String Concatenation) 을 사용합니다.
// 나쁜 예
String username = "hacker' OR '1'='1";
String sql = "SELECT * FROM Users WHERE name = '" + username + "'";
- 완성된 SQL:
SELECT * FROM Users WHERE name = 'hacker' OR '1'='1' - 결과: 데이터베이스는
OR '1'='1'부분을 데이터가 아닌 로직(참인 조건) 으로 인식합니다. 결국 모든 사용자 정보를 반환합니다. 이것이 SQL 인젝션입니다.
"PreparedStatement"의 방어
플레이스홀더(Placeholder, ?) 를 사용합니다.
// 좋은 예
String sql = "SELECT * FROM Users WHERE name = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "hacker' OR '1'='1");
- 결과: 데이터베이스는 입력된 문자열
"hacker' OR '1'='1"전체를 단순한 데이터 값(Literal) 으로 취급합니다. 즉, 이름이 저 이상한 문자열과 정확히 일치하는 사용자를 찾으려 시도합니다. 당연히 결과는 없습니다. 해킹 실패.
4. 상세 비교표
| 특징 | Statement | PreparedStatement |
|---|---|---|
| 파라미터 입력 | 문자열에 직접 결합 (Concatenation). | 실행 시점에 플레이스홀더(?)로 전달. |
| 컴파일 | 매번 새로 컴파일함. | 한 번 컴파일하고 재사용함 (Pre-compiled). |
| 성능 | 반복 쿼리 시 느림. | 반복 쿼리 시 빠름 (캐시 히트). |
| 보안 | 🛑 SQL Injection에 취약함. | ✅ SQL Injection에 안전함. |
| 통신 프로토콜 | 텍스트 프로토콜 사용 (부피가 큼). | 주로 바이너리 프로토콜 사용 (컴팩트함). |
| 가독성 | 지저분함 (따옴표 처리가 복잡). | 깔끔함 (로직이 명확히 보임). |
| 사용처 | DDL (CREATE, ALTER). |
DML (SELECT, INSERT, UPDATE). |
5. 언제 Statement를 써야 할까요?
"PreparedStatement가 무조건 좋으니까 이것만 써야지"라고 생각할 수 있습니다. 99%는 맞습니다.
하지만 딱 하나의 예외가 있는데, 바로 DDL (데이터 정의 언어) 입니다.
CREATE TABLE ...ALTER TABLE ...
이런 쿼리들은 프로그램 수명 주기 동안 한 번만 실행되며, 사용자 입력 파라미터를 받지 않고, 캐싱을 통한 성능 이점도 없기 때문에 일반 Statement를 사용해도 무방합니다.
references